// 《围棋》作者版权所有。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package ir

import (
	"cmd/compile/internal/base"
	"cmd/compile/internal/types"
	"cmd/internal/obj"
	"cmd/internal/objabi"
	"cmd/internal/src"
	"fmt"

	"go/constant"
)

// Ident是一个标识符，可能是限定的。
type Ident struct {
	miniExpr
	sym *types.Sym
}

func NewIdent(pos src.XPos, sym *types.Sym) *Ident {
	n := new(Ident)
	n.op = ONONAME
	n.pos = pos
	n.sym = sym
	return n
}

func (n *Ident) Sym() *types.Sym { return n.sym }

func (*Ident) CanBeNtype() {}

type Name struct {
	miniExpr
	BuiltinOp Op         // uint8 
	Class     Class      // uint8 
	pragma    PragmaFlag // int16 
	flags     bitset16
	DictIndex uint16 // 描述此变量声明类型的字典项索引加上1 
	sym       *types.Sym
	Func      *Func // TODO（austin）：对于I.M、eqFor、hashfor和hashmem 
	Offset_   int64
	val       constant.Value
	Opt       interface{} // 用于转义分析
	Embed     *[]Embed    // 嵌入文件列表，ONAME var 

	PkgName *PkgName // 进口真实包装。名称
	// 对于局部变量（非参数）或外部变量，初始化赋值（OAS或OAS2）。
	// 对于闭包变量，外部捕获变量的ONAME节点。
	// 对于类型开关的局部变量，类型开关保护（OTYPESW）。
	// 对于范围变量，range语句（橙色）
	// 对于recv变量，对于select语句，接收赋值（OSELRECV2）
	// 对于函数名，指向相应的Func节点。
	Defn Node

	// 声明局部变量或参数的函数、方法或闭包。
	Curfn *Func

	Ntype    Ntype
	Heapaddr *Name // temp holding param的堆地址

	// ONAME closure linkage 
	// 考虑：
	// 
	// func f（）{
	// x:=1 
	// func（）{
	// use（x）
	// func（）{
	// use（x）语法分析器在这里--
	// /}（）
	// /}（）
	// /}
	// /
	// /有一个x的原始声明，然后是对x的一系列提及，导致当前函数。每次在一个新闭包中提到x时，我们都会创建一个代表x的变量，用于该特定闭包中，因为在每个闭包中到达x的方式不同。
	// 
	// 让我们对代码中显示的特定变量进行编号：
	// x1是原始的x，x2是在闭包中提到的，
	// x3是在闭包中提到的。
	// 
	// 我们保持这些链接（假设N>1）：
	// 
	// /-x1。Defn=x（与大多数变量一样）的原始声明语句
	// /-x1。最内层=当前最内层闭包x（在本例中为x3），或无
	// /-x1为零。IsClosureVar（）=false 
	// 
	// /-xN。Defn=x1，N>1 
	// /-xN。IsClosureVar（）=true，N>1 
	// /-x2。外部=无
	// /-xN。Outer=x（N-1），N>2 
	// 
	// 
	// 当我们在符号表中查找x时，我们总是得到x1。
	// 然后我们可以使用x1。Innermost（如果不是nil）要获取最内部已知闭包函数的x 
	// 
	// 但是闭包中的第一个引用将找不到x1。最里面的
	// 或x1。最里面的。Funcdepth<Funcdepth。在这种情况下，必须创建一个新的xN，链接为：
	// 
	// xN。Defn=x1 
	// xN。外部=x1。最里面的
	// x1。最里面的=xN 
	// 
	// 当我们完成函数时，我们将处理它的闭包变量
	// 并找到xN，然后使用：
	// 
	// x1:=xN将其从列表中弹出。Defn 
	// x1。最里面的=xN。外部
	// 
	// 我们离开x1。最里面的设置，这样我们仍然可以快速获得原始
	// 变量。这里没有显示，但一旦我们解析完一个函数，就不再需要xN了。对于上述
	// 词汇x参考链接，funcLit 
	// 重新计算xN。外部作为语义x引用链接树，
	// 甚至在中间闭包中填充x，这可能不是
	// 在内部闭包中提到的。
	// 有关详细信息，请参见funcLit。
	// 
	// 在最终编译期间，对于闭包变量，我们有：
	// 
	// xN。Defn=原始变量
	// xN。Outer=在下一个外向作用域
	// /中捕获的变量，用于在xN出现
	// 
	// 由于节点的碎片分片，x.Defn表示x.Name。Defn 
	// 和x.innerst/Outer表示x.Name。帕拉姆。最里面/最外面。
	Innermost *Name
	Outer     *Name
}

func (n *Name) isExpr() {}

func (n *Name) copy() Node                         { panic(n.no("copy")) }
func (n *Name) doChildren(do func(Node) bool) bool { return false }
func (n *Name) editChildren(edit func(Node) Node)  {}

// TypeDefn返回命名OTYPE的类型定义。
// 也就是说，给定“type T Defn”，它返回Defn。
// 它由包类型使用。
func (n *Name) TypeDefn() *types.Type {
	if n.Ntype != nil {
		return n.Ntype.Type()
	}
	return n.Type()
}

// RecordFrameOffset记录名称的帧偏移量。
// 在布置函数参数时，包类型使用它。
func (n *Name) RecordFrameOffset(offset int64) {
	n.SetFrameOffset(offset)
}

// NewNameAt返回一个新的ONAME节点，该节点与位置处的符号s关联。
// 调用者负责设置Curfn。
func NewNameAt(pos src.XPos, sym *types.Sym) *Name {
	if sym == nil {
		base.Fatalf("NewNameAt nil")
	}
	return newNameAt(pos, ONAME, sym)
}

// NewIota返回一个新的OIOTA节点。
func NewIota(pos src.XPos, sym *types.Sym) *Name {
	if sym == nil {
		base.Fatalf("NewIota nil")
	}
	return newNameAt(pos, OIOTA, sym)
}

// NewDeclNameAt返回一个与位置处的符号s关联的新名称。
// 调用者负责设置Curfn。
func NewDeclNameAt(pos src.XPos, op Op, sym *types.Sym) *Name {
	if sym == nil {
		base.Fatalf("NewDeclNameAt nil")
	}
	switch op {
	case ONAME, OTYPE, OLITERAL:
		// ok 
	default:
		base.Fatalf("NewDeclNameAt op %v", op)
	}
	return newNameAt(pos, op, sym)
}

// NewConstAt返回一个与位置处的符号s关联的新OLITERAL节点。
func NewConstAt(pos src.XPos, sym *types.Sym, typ *types.Type, val constant.Value) *Name {
	if sym == nil {
		base.Fatalf("NewConstAt nil")
	}
	n := newNameAt(pos, OLITERAL, sym)
	n.SetType(typ)
	n.SetVal(val)
	return n
}

// newNameAt类似于newNameAt，但允许sym==nil。
func newNameAt(pos src.XPos, op Op, sym *types.Sym) *Name {
	n := new(Name)
	n.op = op
	n.pos = pos
	n.sym = sym
	return n
}

func (n *Name) Name() *Name         { return n }
func (n *Name) Sym() *types.Sym     { return n.sym }
func (n *Name) SetSym(x *types.Sym) { n.sym = x }
func (n *Name) SubOp() Op           { return n.BuiltinOp }
func (n *Name) SetSubOp(x Op)       { n.BuiltinOp = x }
func (n *Name) SetFunc(x *Func)     { n.Func = x }
func (n *Name) Offset() int64       { panic("Name.Offset") }
func (n *Name) SetOffset(x int64) {
	if x != 0 {
		panic("Name.SetOffset")
	}
}
func (n *Name) FrameOffset() int64     { return n.Offset_ }
func (n *Name) SetFrameOffset(x int64) { n.Offset_ = x }
func (n *Name) Iota() int64            { return n.Offset_ }
func (n *Name) SetIota(x int64)        { n.Offset_ = x }
func (n *Name) Walkdef() uint8         { return n.bits.get2(miniWalkdefShift) }
func (n *Name) SetWalkdef(x uint8) {
	if x > 3 {
		panic(fmt.Sprintf("cannot SetWalkdef %d", x))
	}
	n.bits.set2(miniWalkdefShift, x)
}

func (n *Name) Linksym() *obj.LSym               { return n.sym.Linksym() }
func (n *Name) LinksymABI(abi obj.ABI) *obj.LSym { return n.sym.LinksymABI(abi) }

func (*Name) CanBeNtype()    {}
func (*Name) CanBeAnSSASym() {}
func (*Name) CanBeAnSSAAux() {}

// Pragma返回p的PragmaFlag，它必须用于一个OTYPE。
func (n *Name) Pragma() PragmaFlag { return n.pragma }

// SetPragma为p设置PragmaFlag，p必须用于OTYPE。
func (n *Name) SetPragma(flag PragmaFlag) { n.pragma = flag }

// Alias报告p是否为类型别名，p必须是一个OTYPE。
func (n *Name) Alias() bool { return n.flags&nameAlias != 0 }

// SetAlias设置p是否为类型别名，p必须是一个OTYPE。
func (n *Name) SetAlias(alias bool) { n.flags.set(nameAlias, alias) }

const (
	nameReadonly                 = 1 << iota
	nameByval                    // 是通过值或引用捕获的变量
	nameNeedzero                 // 如果它包含指针，需要在函数项上归零
	nameAutoTemp                 // 是临时变量（表示没有矮人信息。如果转义到堆，则重置）
	nameUsed                     // 对于声明的变量和未使用的错误
	nameIsClosureVar             // PAUTOCHEAP CLOSE伪变量；n.Defn 
	nameIsOutputParamHeapAddr    // 指向结果参数的堆复制
	nameIsOutputParamInRegisters // 寄存器中的输出参数的原始（如果有）地址溢出为自动
	nameAddrtaken                // 地址，即使没有移动到inliner创建的堆
	nameInlFormal                // PAUTO，派生自inliner创建的被调用方形式
	nameInlLocal                 // PAUTO，派生自被调用方本地
	nameOpenDeferSlot            // 如果存储开放编码信息的临时变量延迟
	nameLibfuzzerExtraCounter    // 如果应将PEXTERN分配给_ulibfuzzer_extra_counters部分
	nameAlias                    // 是类型名别名
)

func (n *Name) Readonly() bool                 { return n.flags&nameReadonly != 0 }
func (n *Name) Needzero() bool                 { return n.flags&nameNeedzero != 0 }
func (n *Name) AutoTemp() bool                 { return n.flags&nameAutoTemp != 0 }
func (n *Name) Used() bool                     { return n.flags&nameUsed != 0 }
func (n *Name) IsClosureVar() bool             { return n.flags&nameIsClosureVar != 0 }
func (n *Name) IsOutputParamHeapAddr() bool    { return n.flags&nameIsOutputParamHeapAddr != 0 }
func (n *Name) IsOutputParamInRegisters() bool { return n.flags&nameIsOutputParamInRegisters != 0 }
func (n *Name) Addrtaken() bool                { return n.flags&nameAddrtaken != 0 }
func (n *Name) InlFormal() bool                { return n.flags&nameInlFormal != 0 }
func (n *Name) InlLocal() bool                 { return n.flags&nameInlLocal != 0 }
func (n *Name) OpenDeferSlot() bool            { return n.flags&nameOpenDeferSlot != 0 }
func (n *Name) LibfuzzerExtraCounter() bool    { return n.flags&nameLibfuzzerExtraCounter != 0 }

func (n *Name) setReadonly(b bool)                 { n.flags.set(nameReadonly, b) }
func (n *Name) SetNeedzero(b bool)                 { n.flags.set(nameNeedzero, b) }
func (n *Name) SetAutoTemp(b bool)                 { n.flags.set(nameAutoTemp, b) }
func (n *Name) SetUsed(b bool)                     { n.flags.set(nameUsed, b) }
func (n *Name) SetIsClosureVar(b bool)             { n.flags.set(nameIsClosureVar, b) }
func (n *Name) SetIsOutputParamHeapAddr(b bool)    { n.flags.set(nameIsOutputParamHeapAddr, b) }
func (n *Name) SetIsOutputParamInRegisters(b bool) { n.flags.set(nameIsOutputParamInRegisters, b) }
func (n *Name) SetAddrtaken(b bool)                { n.flags.set(nameAddrtaken, b) }
func (n *Name) SetInlFormal(b bool)                { n.flags.set(nameInlFormal, b) }
func (n *Name) SetInlLocal(b bool)                 { n.flags.set(nameInlLocal, b) }
func (n *Name) SetOpenDeferSlot(b bool)            { n.flags.set(nameOpenDeferSlot, b) }
func (n *Name) SetLibfuzzerExtraCounter(b bool)    { n.flags.set(nameLibfuzzerExtraCounter, b) }

// OnStack报告变量n是否可能驻留在堆栈上。
func (n *Name) OnStack() bool {
	if n.Op() == ONAME {
		switch n.Class {
		case PPARAM, PPARAMOUT, PAUTO:
			return n.Esc() != EscHeap
		case PEXTERN, PAUTOHEAP:
			return false
		}
	}
	// 注：fmt。go:dumpNodeHeader调用所有“func（）bool”类型的
	// 方法，但它只能从恐慌中恢复，而不能从Fatalf中恢复。
	panic(fmt.Sprintf("%v: not a variable: %v", base.FmtPos(n.Pos()), n))
}

// MarkReadonly表示n是一个具有只读内容的ONAME。
func (n *Name) MarkReadonly() {
	if n.Op() != ONAME {
		base.Fatalf("Node.MarkReadonly %v", n.Op())
	}
	n.setReadonly(true)
	// 立即将linksym标记为只读
	// 以便SSA后端可以使用此信息。
	// 稍后在转储globls期间将覆盖它。
	n.Linksym().Type = objabi.SRODATA
}

// Val返回常量。节点的值。
func (n *Name) Val() constant.Value {
	if n.val == nil {
		return constant.MakeUnknown()
	}
	return n.val
}

// SetVal设置常数。节点的值。
func (n *Name) SetVal(v constant.Value) {
	if n.op != OLITERAL {
		panic(n.no("SetVal"))
	}
	AssertValidTypeForConst(n.Type(), v)
	n.val = v
}

// Canonical返回n表示的逻辑声明。如果n 
// 是一个闭包变量，那么Canonical将返回原始名称
// 它出现在立即包含
// 声明的函数中。否则，Canonical只返回n本身。
func (n *Name) Canonical() *Name {
	if n.IsClosureVar() && n.Defn != nil {
		n = n.Defn.(*Name)
	}
	return n
}

func (n *Name) SetByval(b bool) {
	if n.Canonical() != n {
		base.Fatalf("SetByval called on non-canonical variable: %v", n)
	}
	n.flags.set(nameByval, b)
}

func (n *Name) Byval() bool {
	// 我们要求在规范变量上设置byval，但我们允许从任何实例访问它。
	return n.Canonical().flags&nameByval != 0
}

// NewClosureVar为fn返回一个新的闭包变量，以引用
// 外部变量n.
func NewClosureVar(pos src.XPos, fn *Func, n *Name) *Name {
	c := NewNameAt(pos, n.Sym())
	c.Curfn = fn
	c.Class = PAUTOHEAP
	c.SetIsClosureVar(true)
	c.Defn = n.Canonical()
	c.Outer = n

	c.SetType(n.Type())
	c.SetTypecheck(n.Typecheck())

	fn.ClosureVars = append(fn.ClosureVars, c)

	return c
}

// NewHiddenParam为fn返回一个新的隐藏参数，该参数具有给定的
// 名称和类型。
func NewHiddenParam(pos src.XPos, fn *Func, sym *types.Sym, typ *types.Type) *Name {
	if fn.OClosure != nil {
		base.FatalfAt(fn.Pos(), "cannot add hidden parameters to closures")
	}

	fn.SetNeedctxt(true)

	// 创建一个与任何实函数无关的假参数，以假装捕获。
	fake := NewNameAt(pos, sym)
	fake.Class = PPARAM
	fake.SetType(typ)
	fake.SetByval(true)

	return NewClosureVar(pos, fn, fake)
}

// CaptureName从函数
// fn或从包块返回适合引用n的名称（如果fn为nil）。如果n是一个自由变量，在包含fn的函数中声明为
// 则CaptureName返回引用fn中n的闭包
// 变量，必要时创建它。
// 否则，它只返回n.
func CaptureName(pos src.XPos, fn *Func, n *Name) *Name {
	if n.Op() != ONAME || n.Curfn == nil {
		return n // 可以直接使用
	}
	if n.IsClosureVar() {
		base.FatalfAt(pos, "misuse of CaptureName on closure variable: %v", n)
	}

	c := n.Innermost
	if c == nil {
		c = n
	}
	if c.Curfn == fn {
		return c
	}

	if fn == nil {
		base.FatalfAt(pos, "package-block reference to %v, declared in %v", n, n.Curfn)
	}

	// 还没有活动闭包的闭包变量；做一个。
	c = NewClosureVar(pos, fn, c)

	// 链接到活动闭包变量列表。
	// 从FinishCaptureNames列表中弹出。
	n.Innermost = c

	return c
}

// FinishCaptureNames处理调用CaptureName 
// 。outerfn应该是直接包含fn的函数。
func FinishCaptureNames(pos src.XPos, outerfn, fn *Func) {
	// 闭包特定变量挂在
	// 普通变量上；请参阅上面的CaptureName。
	// 解开它们。
	// 列出闭包调用的指针列表。
	for _, cv := range fn.ClosureVars {
		// 与n断开连接；请参阅上面关于这些字段的类型名称的注释。
		n := cv.Defn.(*Name)
		n.Innermost = cv.Outer

		// 如果n的闭包用法不是稠密的，我们需要通过在封闭函数中重新捕获n使其稠密。
		// 也就是说，假设我们刚刚解析完代码中最里面的
		// 闭包f4:
		// 
		// func f（）{
		// n:=1 
		// func（）{
		// use（n）
		// func（）{
		// func（）{
		// use（n）
		// n，在这种情况下，我们需要使用CaptureName创建f3的n。
		// 
		// 我们稍后将在walk中决定是直接使用v还是使用&v。
		cv.Outer = CaptureName(pos, outerfn, n)
	}
}

// 同一资源报告两个节点是否引用同一源
// 元素。
// 
// 它的存在有助于将编译器增量迁移到
// 允许引入IdentExpr（#42990）。一旦我们有了
// IdentExpr，直接比较节点
// 值来判断它们是否引用了相同的名称就不再安全了。相反，代码将
// 需要显式获取对底层名称对象的引用，
// 并进行比较。
// 
// 直接比较节点以检查两个
// 节点的语法是否相同仍然是安全的。与
// 相同的源函数表示有意比较节点的语法
// 相等的代码，而不是尚未在
// IdentExpr准备中更新的代码。
func SameSource(n1, n2 Node) bool {
	return n1 == n2
}

// 使用报告表达式x是否是给定
// 变量的（直接）使用。
func Uses(x Node, v *Name) bool {
	if v == nil || v.Op() != ONAME {
		base.Fatalf("RefersTo bad Name: %v", v)
	}
	return x.Op() == ONAME && x.Name() == v
}

// DeclaredBy报告表达式x是否（直接）引用给定语句声明的
// 变量。
func DeclaredBy(x, stmt Node) bool {
	if stmt == nil {
		base.Fatalf("DeclaredBy nil")
	}
	return x.Op() == ONAME && SameSource(x.Name().Defn, stmt)
}

// 变量/函数的类描述变量或函数的“存储类”。在解析过程中，存储类被称为声明上下文。
type Class uint8

// go:generate stringer-type=类名。去
const (
	Pxxx       Class = iota // 不上课；在ssa转换期间用于指示伪变量
	PEXTERN                 // 全局变量
	PAUTO                   // 局部变量
	PAUTOHEAP               // 移动到堆的局部变量或参数
	PPARAM                  // 输入参数
	PPARAMOUT               // 输出结果
	PTYPEPARAM              // 类型参数
	PFUNC                   // 全局函数

	// 小心：类存储在节点中有三位。旗帜。
	_ = uint((1 << 3) - iota) // 物联网的静态断言<=（1<<3）
)

type Embed struct {
	Pos      src.XPos
	Patterns []string
}

// 包是指导入包的标识符。
type PkgName struct {
	miniNode
	sym  *types.Sym
	Pkg  *types.Pkg
	Used bool
}

func (p *PkgName) Sym() *types.Sym { return p.sym }

func (*PkgName) CanBeNtype() {}

func NewPkgName(pos src.XPos, sym *types.Sym, pkg *types.Pkg) *PkgName {
	p := &PkgName{sym: sym, Pkg: pkg}
	p.op = OPACK
	p.pos = pos
	return p
}
